import { ExecutionContext, Injectable } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { AuthGuard } from '@nestjs/passport';
import { IS_PUBLIC_KEY } from '../core/decorators/publicGuards';
import { ExtractJwt } from 'passport-jwt';
import { TokenService } from '../User/service/token.service';
import { isNil } from 'lodash';

/**
 * jwt验证
 */
@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
  constructor(
    private tokenService: TokenService,
    private reflector: Reflector,
  ) {
    super();
  }
  async canActivate(context: ExecutionContext) {
    const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
      context.getHandler(),
      context.getClass(),
    ]);
    // 1 判断是否需要使用token  如果不需要直接返回true
    if (isPublic) return true;
    const request = context.switchToHttp().getRequest();
    const response = context.switchToHttp().getResponse();
    // 2 如果需要token 判断请求头中是否含有token 如果有继续 没有返回401
    const requestToken = ExtractJwt.fromAuthHeaderAsBearerToken()(request);
    console.log(requestToken, 'requestToken');

    if (!requestToken) return false;
    // 3 如果有token判断token是否过期 没有过期 返回true
    try {
      const is_expired = await super.canActivate(context);
      return is_expired as boolean;
    } catch (error) {
      // 4 如果过期 判断refreshtoken是否过期 如果过期返回token过期 401
      const accessToken = await this.tokenService.checkAccessToken(
        requestToken,
      );
      console.log(accessToken, 'accessToken');

      if (isNil(accessToken)) return false;
      const newToken = await this.tokenService.refreshToken(
        accessToken,
        response,
      );
      if (!newToken) return false;
      if (newToken.accessToken) {
        request.headers.authorization = `Bearer ${newToken.accessToken.value}`;
      }
      response.header('Access-Control-Expose-Headers', 'token');
      const result = await super.canActivate(context);
      // 5 如果没有过期 那么生成新的token然后删除之前过期的token 重新绑定token和refreshtoken从响应头中返回给前端 返回true
      if (!newToken) return false;
      return result as boolean;
    }
  }
}
